#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>

#include "ota.h"
#include "public/errno.h"
#include "ymodem.h"
#include "cmsis_os2.h"
#include "FreeRTOS.h"
#include "task.h"
#include "elog.h"
#include "flash/flash_main.h"
#include "aadl/include/aadl/flash.h"
#include "misc/misc_api.h"
#include "bsp.h"
#include "ota_ware.h"
#include "aadl/public.h"
#include "hal_adapter.h"
#include "public/errno.h"
#include "bsp_uart.h"
#include "misc/task_misc.h"

#define ENABLE_DEBUG 1
#include "debug.h"

/* 4KB */
#define SECTOR_SIZE     (4 * 1024) 

#if defined ( __GNUC__ )
#define XFA_OFFSET 0
#else
#define XFA_OFFSET 1
#endif

#define TYPE_INSTRUCMENT            0
#define TYPE_BLUETOOTH              1
#define TYPE_ECU                    2   
#define TYPE_BMS                    3
#define TYPE_INSTRUCMENT_UI         5
#define TYPE_IOT                    6
#define TYPE_GNSS                   7
#define TYPE_MODEM                  8 

//typedef struct ota_firmware_xfa
//{
//    unsigned short type;
//    ota_firmware_xfa_t* (*get_iot_xfa)(void);
//}ota_firmware_xfa_t;

static unsigned int             ota_step = 0;
static unsigned int             ota_type = 0;
static unsigned int             ota_subs = 0;
static unsigned int             ota_size = 0;
static unsigned int             ota_addr = 0;
static unsigned int             ota_retry = 0;
static ota_firmware_xfa_t*      ota_ware = NULL;
static int                      ota_result = 0;
static unsigned int             ota_received = 0;
static unsigned int             ota_sent = 0;
static unsigned int             ota_write_offset = 0;
static unsigned int             ota_read_offset = 0;
static int32_t                  sect_offset = 0;
/* 和app_flash_xxx函数相关 */
static uint8_t fw_buff[SECTOR_SIZE] = {0};
static uint32_t offset = 0;

#if 0
static ATTR_APPSDRAM unsigned char ota_buffer_ymodem[PACKET_OVERHEAD + PACKET_1K_SIZE];
static ATTR_APPSDRAM unsigned char ota_buffer_firmware[OTA_FIRMWARE_SDRAM_MAX_SIZE];
#else 
static unsigned char ota_buffer_ymodem[PACKET_OVERHEAD + PACKET_1K_SIZE];
static unsigned char ota_buffer_firmware[OTA_FIRMWARE_SDRAM_MAX_SIZE];
#endif

static unsigned int str_to_u32(const char* str);

static int ota_begin_handler(unsigned char *buf, unsigned int len, struct ymodem *ymodem)
{
    DEBUG("[YMODEM]: BEGIN HANDLER\n");

    if (ota_ware == NULL) {
        return -ENOENT;
    }

    ota_received = 0;
    ota_write_offset = 0;

    char *file_size = (char *)&buf[strlen((const char *)buf) + 1];
    DEBUG("[YMODEM]: file_size: %s\n", file_size);

    /* 计算固件数据大小 */
    ota_size = str_to_u32(file_size);

    /* 检查固件大小 */
    if (ota_size > ota_ware->size) {
        return -EFBIG;
    }

    if (ota_ware->save == 0) {
        /* 保存到SDRAM */
        ota_addr = (unsigned int)ota_buffer_firmware;   //存储起始地址
        ota_write_offset += ota_addr;                   //存储偏移地址
    }
    else if (ota_ware->save == 1) {
        /* 保存到FLASH */
        ota_addr = ota_ware->addr;                      //存储起始地址
        ota_write_offset += ota_addr;                   //存储偏移地址
    }
    else {
        return -ENOMEDIUM;
    }

    return 0;
}


static int ota_data_handler(unsigned char *buf, unsigned int len, struct ymodem *ymodem)
{
    int ret;

    DEBUG("[YMODEM]: DATA HANDLER\n");

    if (ota_ware == NULL) {
        return -ENOENT;;
    }

    /* 保存固件数据 */
    if (ota_ware->save == 0) {
        /* 保存数据到SDRAM */
        //memcpy((void *)ota_write_offset, buf, len);
    }
    else if (ota_ware->save == 1) {
        /* 保存数据到FLASH */
        app_flash_write((uint64_t)ota_received, len, ( unsigned char *)buf);
    }
    else {
        return -ENOMEDIUM;
    }
    
    ota_received += len;        //已经收到数据累加
    ota_write_offset += len;    //存储偏移地址累加
    
    /* 收到第一包数据后，检查固件头部信息 */
    if (ota_received == len) {
        app_flash_flush();
        /* 首包数据检查 */
        ret = ota_ware->check(ota_type, ota_size, ota_ware->save, ota_addr);
       if (0 != ret) {
           return ret;
       }
    }

    return 0;
}


static int ota_end_handler(unsigned char *buf, unsigned int len, struct ymodem *ymodem)
{
    DEBUG("[YMODEM]: END HANDLER\n");
    int ret = 0;

    /* 是否接收完成 */
    if (ota_received + len < ota_size) {
        ret = -ENODATA;
        return ret;
    }

    /* 1为flash存储，2为ram存储 */
    if (ota_ware->save == 1) {
        ret = app_flash_flush();
    }
    else if (ota_ware->save == 2) {
        #if 0
        dat_flash_flush();
        #endif
    }

    return 0;
}


static int ota_send_begin_handler(unsigned char *buf, unsigned int len, struct ymodem *ymodem)
{
    /* 初始化 */
    ota_sent = 0;
    ota_read_offset = ota_addr;


    /* 设置文件名和文件大小 */
    if (ota_type == type_iot) {
        strcpy(ymodem->file_name, "iot_firmware.bin");
    }
    else if (ota_type == type_ble) {
        strcpy(ymodem->file_name, "ble_firmware.bin");
    }    
    else if (ota_type == type_gps) {
        strcpy(ymodem->file_name, "gps_firmware.bin");
    }
    else if (ota_type == type_dis) {
        strcpy(ymodem->file_name, "dis_firmware.bin");
    }
    else {
        strcpy(ymodem->file_name, "unknown.bin");
    }
    ymodem->file_size = ota_size;

    /* 生成文件名称和文件大小数据 */
    memset(buf, 0, len);
    memcpy(buf, ymodem->file_name, strlen(ymodem->file_name));
    memcpy(buf + strlen(ymodem->file_name) + 1, ymodem_u32_to_str(ymodem->file_size), strlen(ymodem_u32_to_str(ymodem->file_size)));

    return 0;
}


static int ota_send_data_handler(unsigned char *buf, unsigned int len, struct ymodem *ymodem)
{
    int res = 0;
    
    /* 生成固件数据 */
    memset(buf, 0, len);
    
    res = app_flash_read(len, buf, ota_sent);
    if (0 > res)
        log_e("App flash read fail, error reason: %s", errno_str(res));

    /* 已发数据和读取位置累加 */
    ota_sent += len;
    ota_read_offset += len;

    return 0;
}


static int ota_send_end_handler(unsigned char *buf, unsigned int len, struct ymodem *ymodem)
{
    /* 生成结束数据 */
    memset(buf, 0, len);
    return 0;
}


static ota_firmware_xfa_t* ota_type_search(unsigned short type)
{
    ota_firmware_xfa_t* ota_ware = NULL;

    for (ota_ware = (ota_firmware_xfa_t*)ota_ware_start;
         ota_ware < (ota_firmware_xfa_t*)ota_ware_end;
         ota_ware++) {
        if (type == ota_ware -> type) {
            return ota_ware;
        }
    }
    
    return NULL;
}


static struct ymodem ymodem = {
    .mode = YMODEM_MODE_SEND,
    .stage = YMODEM_STAGE_NONE,
    .file_name = "",
    .file_size = 0,
    .buffer = ota_buffer_ymodem,
    .rd_len = PACKET_1K_SIZE,
    .timeout = PACKET_TIMEOUT_MS,
    .timestart = 0,
    .time_msec = getms,
    .read = NULL,
    .write = NULL,
    .begin_packet_cb = ota_send_begin_handler,
    .data_packet_cb = ota_send_data_handler,
    .end_packet_cb = ota_send_end_handler,
};

int ota_initize(int (*read)(void *data, int len), int (*write)(void *data, int len), unsigned int (*time_msec)(void))
{
    assert(read != NULL);
    assert(write != NULL);
    assert(time_msec != NULL);

    ymodem.read = read;
    ymodem.write = write;
    ymodem.time_msec = time_msec;

    return 0;
}

int ota_process(bool *is_ota, uint8_t one_byte)
{
    static unsigned int starttime = 0;
    char result[33] = {'\0'};
    int ret;
    int len = 0; 
    char command[32] = {'\0'};
    TaskHandle_t curTaskHandle = xTaskGetCurrentTaskHandle();  
#if 0
    static ATTR_APPSDRAM char buffer[OTA_COMMAND_BUFFER_MAX_SIZE] = {'\0'};
#else 
    static char buffer[OTA_COMMAND_BUFFER_MAX_SIZE] = {'\0'};
#endif
    static unsigned int  rd_len = 0;
    int argc = OTA_COMMAND_PARAMS_MAX_SIZE;
    char *argv[OTA_COMMAND_PARAMS_MAX_SIZE] = {NULL};

    if (ymodem.time_msec == NULL || 
        ymodem.read == NULL ||
        ymodem.write == NULL) {
        return -1;
    }

    switch (ota_step) {
    case 0:     /* 空闲阶段，接收AT+OTASTR启动指令 */
            if (one_byte == '\r' || one_byte == '\n') {
                buffer[rd_len] = '\0';
                rd_len = 0;
                quec_string_split(buffer, strlen(buffer), " =,", &argc, (char **)&argv);
                if (argc > 3 && 0 == strcmp(argv[0], "AT+OTASTR")) {    
                    #if 1
                    tx_buf_clr(comm_uart_id);
                    #else 
                    tx_buf_clr(debug_uart_id);
                    #endif
                    ota_type = (unsigned int)atoi(argv[2]);
                    ota_subs = (unsigned int)atoi(argv[3]);
                    ota_ware = ota_type_search(ota_type);
                    if (ota_ware != NULL && 0 <= ota_type && 6 >= ota_type) {
                        ymodem.write("+OTASTR:OK\r\n", strlen("+OTASTR:OK\r\n"));
                        /* 暂时调高优先级,避免升级失败 */
                        if (uxTaskPriorityGet(curTaskHandle) == osPriorityNormal) {
                            vTaskPrioritySet(curTaskHandle, osPriorityNormal + 4);
                        }                           
                        ota_step = 11;
                        *is_ota = true;
                    }
                    else {
                        ymodem.write("+OTASTR:ERROR\r\n", strlen("+OTASTR:ERROR\r\n"));
                    }
                }
            }
            else {
                if (rd_len >= OTA_COMMAND_BUFFER_MAX_SIZE) {
                    rd_len = 0;
                }
                buffer[rd_len++] = one_byte;
            }
        break;
    case 1:     /* 发送AT+OTASTR启动指令 */
        snprintf(command, sizeof(command), "\r\nAT+OTASTR=0,%u,0\r\n", ota_type);
        tx_buf_clr(comm_uart_id);
        rx_buf_clr(comm_uart_id);
        memset(buffer, 0, sizeof(buffer));
        rd_len = 0;
        ymodem.write(command, strlen(command));
        log_d("upgrade send: %s\r\n", command);

        /* 更新时间、步骤和结果,进入ota升级状态 */
        starttime = bsp_get_ms_since_boot();
        *is_ota = true;
        offset = 0;
        ota_step += 1;
        offset = 0;
        ota_result = -EBUSY;
        if (uxTaskPriorityGet(curTaskHandle) == osPriorityNormal) {
            vTaskPrioritySet(curTaskHandle, osPriorityNormal + 8);
        }           
        break;
    case 2:     /* 等待+OTASTR:OK响应 */
        /* 如果响应超时，则升级失败 */
        if (bsp_get_ms_since_boot() >= starttime + 3000) {
            if (ota_retry++ <=5)
                ota_step = 1;
            else {
                ota_step = 0;
                ota_retry = 0;
                ota_result = -ETIMEDOUT;
                break;
            }
        }

        /* 读取应答数据 */
        len = ymodem.read(&one_byte , 1);
        if (len > 0) {
            if (one_byte == '\r' || one_byte == '\n') {
                buffer[rd_len] = '\0';
                rd_len = 0;
                log_d("upgrade recv: %s\r\n", buffer);
                
                if (0 != strcmp("+OTASTR:OK", buffer)) {
                    ota_step = 0;
                    ota_result = -ENOPROTOOPT;
                    break;
                }

                /* 更新时间、步骤和结果 */
                starttime = bsp_get_ms_since_boot();
                ota_step += 1;
                ota_result = -EBUSY;
                if (uxTaskPriorityGet(curTaskHandle) == osPriorityNormal) {
                    vTaskPrioritySet(curTaskHandle, osPriorityNormal + 4);
                }                 
            } else {
                if (rd_len > sizeof(buffer)) {
                    rd_len = 0;
                }
                buffer[rd_len++] = one_byte;
            }
        }        
        break;
    case 3: /* 执行YMODEM发送数据 */
        ret = ymodem_process(&ymodem);
        if (ret < 0) {
            if (ret != -EBUSY) {
                ota_step = 0;
                ota_result = ret;
                if (uxTaskPriorityGet(curTaskHandle) == osPriorityNormal + 4) {
                    vTaskPrioritySet(curTaskHandle, osPriorityNormal);
                }       
                break;
            }
        }
        else {
            /* 更新时间、步骤和结果 */
            starttime = bsp_get_ms_since_boot();
            ota_step += 1;
            ota_result = -EBUSY;
        }
        break;    
    case 4: /* 等待+OTARES:<type>,<sub_type>,<result> */
        /* 如果等待升级结果超时，则失败 */
        if (bsp_get_ms_since_boot() >= starttime + 1000 * 60) {
            *is_ota = false;
            ota_step = 0;
            ota_result = -ETIMEDOUT;
            break;
        }

        /* 等待升级结果 */
        len = ymodem.read(&one_byte , 1);
        if (len > 0) {
            if (one_byte == 0x06 || one_byte == 0x18) {
                break;
            }

            if (one_byte == '\r' || one_byte == '\n') {
                buffer[rd_len] = '\0';
                rd_len = 0;
                log_d("upgrade recv: %s\r\n", buffer);
                quec_string_split(buffer, strlen(buffer), " :,", &argc, (char **)&argv);
                if (argc > 3 && 0 == strcmp(argv[0], "+OTARES")) {
                    *is_ota = false;
                    ota_step = 0;
                    ota_result = -1 * atoi(argv[3]);
                }
                else {
                    *is_ota = false;
                    ota_step = 0;
                    ota_result = -EUNATCH;
                }
            }
            else {
                if (rd_len >= sizeof(buffer)) {
                    rd_len = 0;
                }
                buffer[rd_len++] = one_byte;
            }
        }
        break;            
    case 11:
        log_d("upgrade switch to recv process\r\n");
        ymodem.mode = YMODEM_MODE_RECV;
        ymodem.rd_len = 0;
        offset = 0;
        ymodem.begin_packet_cb = ota_begin_handler,
        ymodem.data_packet_cb = ota_data_handler,
        ymodem.end_packet_cb = ota_end_handler,
        offset = 0;
        ota_step += 1;  
    case 12:
        ret = ymodem_process(&ymodem);
        if (ret < 0) {
            if (ret != -EBUSY) {
                /* 下载失败，上报错误码 */
                snprintf(result, sizeof(result), "+OTARES:%u,%u\r\n", ota_type, -1 * ret);
                ymodem.write(result, strlen(result));
                goto EXIT;
            }

            /* 下载进行中，保持执行下载 */
        }
        else {
            /* 下载成功，执行下一个阶段 */
            ota_step += 1;        
        }
        break;
    case 13:     /* 更新阶段 */
        ret = ota_ware->update(ota_type, ota_size, ota_ware->save, ota_addr);
        if (ret < 0) {
            if (ret != -EBUSY) {
                log_e("ota failed, subs = %d, result = %d", ota_subs, ota_result);
                /* 固件更新失败，上报错误码 */
                snprintf(result, sizeof(result), "+OTARES:%u,%u\r\n", ota_type, -1 * ret);
                ymodem.write(result, strlen(result));
                goto EXIT;
            }

            /* 固件更新进行中，保持执行更新 */
        }
        else {
            /* 下载成功，恢复优先级 */   
            if (uxTaskPriorityGet(curTaskHandle) == osPriorityNormal + 4) {
                vTaskPrioritySet(curTaskHandle, osPriorityNormal);
            }  

            /* 记录升级文件类型 */
            misc_save_fota_type(ota_type);
  
            ota_step = 0;
            ota_type = 0;
            ota_size = 0;
            ota_ware = NULL;
            *is_ota = false;
            /* 重启系统 */

            if (ota_type == 0) {
                tbox_system_reboot(1000);
            }

            return 0;
        }
        break;    
    default:
        goto EXIT;
        break;
    }

    return 0;

EXIT:
    if(uxTaskPriorityGet(curTaskHandle) == osPriorityNormal + 4){
        vTaskPrioritySet(curTaskHandle, osPriorityNormal);
    }  
    ota_step = 0;
    ota_type = 0;
    ota_size = 0;
    ota_ware = NULL;
    *is_ota = false;

    return -1;
}


int ota_is_idle(void)
{
    if (ota_step == 0) {
        return 1;
    }

    return 0;
}


/* 字符串转U32 */
static unsigned int str_to_u32(const char* str)
{
    const char *s = str;
    unsigned int acc;
    int c;

    /* strip leading spaces if any */
    do {
        c = *s++;
    } while (c == ' ');

    for (acc = 0; (c >= '0') && (c <= '9'); c = *s++) {
        c -= '0';
        acc *= 10;
        acc += c;
    }

    return acc;
}


int32_t app_flash_write(uint64_t ota_received, uint32_t len, unsigned char *buf)
{
    int32_t ret = 0; 
    
    sect_offset = ota_received / SECTOR_SIZE; 
    
    /* 缓存数据，当数据量达到扇区大小，再写入flash */
    if(offset + len < SECTOR_SIZE) {
        memcpy(&fw_buff[offset], buf, len);
        offset += len;
    } else { 
        while(offset + len >= SECTOR_SIZE) {
            /* buff剩余空间填充数据 */
            memcpy(&fw_buff[offset], buf, SECTOR_SIZE- offset);
            len -= SECTOR_SIZE - offset;
            buf += SECTOR_SIZE - offset;
            offset = 0;
            
            /* 写入flash，并读出来及时校验*/ 
            ret = aad_flash_direct_write(FILE_OTA_ID, sect_offset * SECTOR_SIZE, fw_buff, SECTOR_SIZE, AADL_WAIT | AADL_KEEP_OUTSIDE , 0); 
            if (ret != 0)
            {
                log_e("Fail to save iot firmware into flash! id:%d,%d\r\n", FILE_OTA_FLAG_ID, ret);
            }    
            
            sect_offset += 1;
      
        }
        
        /* 将剩余未读取数据放入fw_buff中 */
        memcpy(&fw_buff[offset], buf, len);      
        offset += len;        
    }

    return ret;
}


int8_t app_flash_flush()
{
    int8_t ret = 0;
    
    // sect_offset = ota_received / SECTOR_SIZE; 
    ret = aad_flash_direct_write(FILE_OTA_ID, sect_offset * SECTOR_SIZE, fw_buff, offset, AADL_WAIT | AADL_KEEP_OUTSIDE, 0);
    
    if (ret != 0)
    {
        log_e("Flush remaining iot fw data failed! id:%d,%d\r\n", FILE_OTA_FLAG_ID, ret);
        ret = -ENOENT;
    }
    
    // offset = 0;
    
    return ret;
}


ssize_t app_flash_read(ssize_t len, uint8_t *buf, uint32_t offset)
{
    return aad_flash_direct_read(FILE_OTA_ID, offset, buf, len);
}

uint32_t tbox_ota_flag = 0;
/* 请求升级 */
int iot_upgrade_request(unsigned int type, unsigned int size, unsigned int save, unsigned int addr)
{
    unsigned char one_byte;

    if (!(ota_step == 0 || ota_step == 13)) {
        return -EPERM;
    }

    while(ymodem.read(&one_byte , 1) > 0) {};

    log_d("upgrade switch to send process\r\n");
    ymodem.mode = YMODEM_MODE_SEND;
    ymodem.rd_len = PACKET_1K_SIZE;
    ymodem.begin_packet_cb = ota_send_begin_handler,
    ymodem.data_packet_cb = ota_send_data_handler,
    ymodem.end_packet_cb = ota_send_end_handler,

    ota_type = type;
    ota_size = size;
    ota_addr = addr;

    ota_step = 1;
    ota_result = -EBUSY;
    
    tbox_ota_flag = 1;

    return 0;
}
